﻿using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using Unicorn.Internal;
using Unicorn;
using Unicorn.Arm;
using System;
using System.Runtime.InteropServices;

public unsafe class Uc : arm
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }

    public static uc_engine engine;

    public static uc_err uc_emu_start(uc_engine engine, long startAddr, long stopAddr, int v, int v2)
    {
        print("start:" + startAddr + "  stop:" + stopAddr);
        engine.emulator.Start((ulong)startAddr, (ulong)stopAddr);
        return engine.err;
    }

    public delegate void hook_codes(uc_engine uc, ulong address, uint size, object user_data);
    public delegate bool hook_mem_invalids(uc_engine uc, uc_mem_type type, ulong address, int size, long value, object user_data);
    static MemoryHookCallback hook_codesCallback;
    static MemoryHookCallback hook_mem_invalidsCallback;

    public static void codes(Emulator emulator, MemoryType type, ulong address, int size, ulong value, object userToken)
     {
        HC(Uc.engine, address, (uint)size, value);
    }

    public static void mem_invalids(Emulator emulator, MemoryType type, ulong address, int size, ulong value, object userToken)
    {
        HM(Uc.engine,(uc_mem_type)type, address, size, (long)value,userToken);
    }

    public static hook_codes HC;
    public static hook_mem_invalids HM;

    public static void HookBlock(Emulator emu, ulong address, int size, object userToken)
    {
        Console.WriteLine($">>> Tracing basic block at 0x{address.ToString("x2")}, block size = 0x{size.ToString("x2")}");
    }

    public static void HookCode(Emulator emu, ulong address, int size, object userToken)
    {

        Console.WriteLine($">>> Tracing instruction at 0x{address.ToString("x2")}, instruction size = 0x{size.ToString("x2")}");

        HC(Uc.engine, address, (uint)size, userToken);
    }

    private static void HookInterrupt(Emulator emulator, int into, object userData)
    {
        HM(Uc.engine, uc_mem_type.UC_MEM_READ, (ulong)into, 0, 0, userData);
    }

    public static void uc_hook_add(uc_engine engine, uc_hook trace,int mode, hook_mem_invalids hook_Mem,object obj,int v,int v2, int v3)
    {
        HM += hook_Mem;
        hook_mem_invalidsCallback += mem_invalids;
         engine.emulator.Hooks.Memory.Add((MemoryHookType)mode, hook_mem_invalidsCallback, obj);
        //engine.emulator.Hooks..Interrupt.Add(HookInterrupt, obj);
    }

    public static void uc_hook_add(uc_engine engine, uc_hook trace, int mode, hook_codes hook_Mem, object obj, int v, int v2)
    {
        HC += hook_Mem;
        hook_codesCallback += codes;
        //engine.emulator.Hooks.Code.Add((MemoryHookType)mode, hook_codesCallback, obj);
        engine.emulator.Hooks.Code.Add(HookCode, obj);
    }

    public static void uc_close(uc_engine engine)
    {
        
    }

    public static uc_err uc_mem_map_ptr(uc_engine engine, long v,int v2, uc_prot prot,byte* mem)
    {
        engine.emulator.Memory.Map((ulong)v,v2, MemoryPermissions.All);
        
        return engine.err;
    }

    public static string uc_strerror(uc_err err)
    {
        return "uc_strerror";
    }

    public static uc_err uc_open(int i, int m, out uc_engine Engine)
    {
        Engine =   new uc_engine();
        Uc.engine = Engine;
        return Uc.engine.err;
    }

    public static void uc_reg_read(uc_engine engine, uc_arm_reg id, out int v)
    {
        uint a;
        v = (int)uc_reg_read(engine,id, out a);
    }

    public static uint uc_reg_read(uc_engine engine, uc_arm_reg id, out uint v)
    {
        v = (uint)engine.emulator.Registers.Read((int)id);
        return v;
    }

    public static void uc_reg_write(uc_engine engine, uc_arm_reg id, long value)
    {
        engine.emulator.Registers.Write((int)id,value);
    }

    public static void uc_mem_read(uc_engine engine,uint addr,out uint v,int size)
    {
        byte[] date = new byte[size];
            engine.emulator.Memory .Read((uint)addr,date,size);
        v = *(uint*)(getBytePtr(date));
    }

    public static void uc_mem_read(uc_engine engine, uint addr, sbyte* v, int size)
    {
        byte[] date = new byte[size];
        engine.emulator.Memory.Read((uint)addr, date, size);
        v = (sbyte*)(getBytePtr(date));
    }

    public static uc_err uc_mem_write(uc_engine engine, long addr, byte[] bytes, int size)
    {
        engine.emulator.Memory.Write((ulong)addr, bytes, (int)size);
        return engine.err;
    }

    public static uc_err uc_mem_write(uc_engine engine, long addr, void* bytes, uint size)
    {
        engine.emulator.Memory.Write((ulong)addr, Getbyte( (byte*)bytes,size), (int)size);
        return engine.err;
    }

    public static byte[] Getbyte(byte* bp,uint len)
    {
        if (len == 4)
        {
            uint num = *(uint*)bp;
            byte[] bytes = BitConverter.GetBytes(num);
            //return bytes;
        }
        byte[] barr = new byte[len];
        Marshal.Copy((IntPtr)bp,  barr,0, (int)len);
        for(int i=0;i<4;i++)
        {
            byte b = barr[i];
        }
        
        //memcpy(bp,0, barr, 0,len);
        return barr;
    }

    

    public static int pthread_mutex_init(Mutex mutex,object o)
    {
        if (mutex.InitializeLifetimeService() == o)
            return -1;
        else
            return 0;
    }

    public static int pthread_mutex_lock(Mutex mutex)
    {
        if (mutex.WaitOne())
            return -1;
        else
            return 0;
    }

    public static int pthread_mutex_unlock(Mutex mutex)
    {
        mutex.ReleaseMutex();
            return 0;
    }

    public static void perror(string info)
    {
        Console.Write(info);
    }
}

public enum uc_arm_reg
{
    UC_ARM_REG_INVALID = 0,
    UC_ARM_REG_APSR,
    UC_ARM_REG_APSR_NZCV,
    UC_ARM_REG_CPSR,
    UC_ARM_REG_FPEXC,
    UC_ARM_REG_FPINST,
    UC_ARM_REG_FPSCR,
    UC_ARM_REG_FPSCR_NZCV,
    UC_ARM_REG_FPSID,
    UC_ARM_REG_ITSTATE,
    UC_ARM_REG_LR,
    UC_ARM_REG_PC,
    UC_ARM_REG_SP,
    UC_ARM_REG_SPSR,
    UC_ARM_REG_D0,
    UC_ARM_REG_D1,
    UC_ARM_REG_D2,
    UC_ARM_REG_D3,
    UC_ARM_REG_D4,
    UC_ARM_REG_D5,
    UC_ARM_REG_D6,
    UC_ARM_REG_D7,
    UC_ARM_REG_D8,
    UC_ARM_REG_D9,
    UC_ARM_REG_D10,
    UC_ARM_REG_D11,
    UC_ARM_REG_D12,
    UC_ARM_REG_D13,
    UC_ARM_REG_D14,
    UC_ARM_REG_D15,
    UC_ARM_REG_D16,
    UC_ARM_REG_D17,
    UC_ARM_REG_D18,
    UC_ARM_REG_D19,
    UC_ARM_REG_D20,
    UC_ARM_REG_D21,
    UC_ARM_REG_D22,
    UC_ARM_REG_D23,
    UC_ARM_REG_D24,
    UC_ARM_REG_D25,
    UC_ARM_REG_D26,
    UC_ARM_REG_D27,
    UC_ARM_REG_D28,
    UC_ARM_REG_D29,
    UC_ARM_REG_D30,
    UC_ARM_REG_D31,
    UC_ARM_REG_FPINST2,
    UC_ARM_REG_MVFR0,
    UC_ARM_REG_MVFR1,
    UC_ARM_REG_MVFR2,
    UC_ARM_REG_Q0,
    UC_ARM_REG_Q1,
    UC_ARM_REG_Q2,
    UC_ARM_REG_Q3,
    UC_ARM_REG_Q4,
    UC_ARM_REG_Q5,
    UC_ARM_REG_Q6,
    UC_ARM_REG_Q7,
    UC_ARM_REG_Q8,
    UC_ARM_REG_Q9,
    UC_ARM_REG_Q10,
    UC_ARM_REG_Q11,
    UC_ARM_REG_Q12,
    UC_ARM_REG_Q13,
    UC_ARM_REG_Q14,
    UC_ARM_REG_Q15,
    UC_ARM_REG_R0,
    UC_ARM_REG_R1,
    UC_ARM_REG_R2,
    UC_ARM_REG_R3,
    UC_ARM_REG_R4,
    UC_ARM_REG_R5,
    UC_ARM_REG_R6,
    UC_ARM_REG_R7,
    UC_ARM_REG_R8,
    UC_ARM_REG_R9,
    UC_ARM_REG_R10,
    UC_ARM_REG_R11,
    UC_ARM_REG_R12,
    UC_ARM_REG_S0,
    UC_ARM_REG_S1,
    UC_ARM_REG_S2,
    UC_ARM_REG_S3,
    UC_ARM_REG_S4,
    UC_ARM_REG_S5,
    UC_ARM_REG_S6,
    UC_ARM_REG_S7,
    UC_ARM_REG_S8,
    UC_ARM_REG_S9,
    UC_ARM_REG_S10,
    UC_ARM_REG_S11,
    UC_ARM_REG_S12,
    UC_ARM_REG_S13,
    UC_ARM_REG_S14,
    UC_ARM_REG_S15,
    UC_ARM_REG_S16,
    UC_ARM_REG_S17,
    UC_ARM_REG_S18,
    UC_ARM_REG_S19,
    UC_ARM_REG_S20,
    UC_ARM_REG_S21,
    UC_ARM_REG_S22,
    UC_ARM_REG_S23,
    UC_ARM_REG_S24,
    UC_ARM_REG_S25,
    UC_ARM_REG_S26,
    UC_ARM_REG_S27,
    UC_ARM_REG_S28,
    UC_ARM_REG_S29,
    UC_ARM_REG_S30,
    UC_ARM_REG_S31,

    UC_ARM_REG_C1_C0_2,
    UC_ARM_REG_C13_C0_2,
    UC_ARM_REG_C13_C0_3,

    UC_ARM_REG_IPSR,
    UC_ARM_REG_MSP,
    UC_ARM_REG_PSP,
    UC_ARM_REG_CONTROL,
    UC_ARM_REG_ENDING,		// <-- mark the end of the list or registers

    //> alias registers
    UC_ARM_REG_R13 = UC_ARM_REG_SP,
    UC_ARM_REG_R14 = UC_ARM_REG_LR,
    UC_ARM_REG_R15 = UC_ARM_REG_PC,

    UC_ARM_REG_SB = UC_ARM_REG_R9,
    UC_ARM_REG_SL = UC_ARM_REG_R10,
    UC_ARM_REG_FP = UC_ARM_REG_R11,
    UC_ARM_REG_IP = UC_ARM_REG_R12,
}

public enum uc_err_type
{
    UC_ERR_OK = 0,   // No error: everything was fine
    UC_ERR_NOMEM,      // Out-Of-Memory error: uc_open(), uc_emulate()
    UC_ERR_ARCH,     // Unsupported architecture: uc_open()
    UC_ERR_HANDLE,   // Invalid handle
    UC_ERR_MODE,     // Invalid/unsupported mode: uc_open()
    UC_ERR_VERSION,  // Unsupported version (bindings)
    UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start()
    UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start()
    UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start()
    UC_ERR_HOOK,    // Invalid hook type: uc_hook_add()
    UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
    UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
    UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start()
    UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start()
    UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start()
    UC_ERR_ARG,     // Inavalid argument provided to uc_xxx function (See specific function API)
    UC_ERR_READ_UNALIGNED,  // Unaligned read
    UC_ERR_WRITE_UNALIGNED,  // Unaligned write
    UC_ERR_FETCH_UNALIGNED,  // Unaligned fetch
    UC_ERR_HOOK_EXIST,  // hook for this event already existed
    UC_ERR_RESOURCE,    // Insufficient resource: uc_emu_start()
    UC_ERR_EXCEPTION, // Unhandled CPU exception
}

//[StructLayout(LayoutKind.Sequential)]
public class uc_engine
{
    public uc_err err;
    public uc_hook hook;
    public Emulator emulator;
    public uc_engine()
    {
        emulator = new ArmEmulator(ArmMode.Arm);
    }
}

//public class uc_err
//{
//    public uc_err_type type;
//    public static implicit operator uc_err(uc_err_type type)
//    {
//        return new uc_err() { type = type  };
//    }
//}

public class uc_hook
{

}

public enum uc_mem_type
{
    UC_MEM_READ=16,
    
	UC_MEM_WRITE,
			
		 UC_MEM_FETCH,
		
		 UC_MEM_READ_UNMAPPED,
			
		 UC_MEM_WRITE_UNMAPPED,
			
		 UC_MEM_FETCH_UNMAPPED,
			
		 UC_MEM_WRITE_PROT,
			
		 UC_MEM_READ_PROT,
			
		 UC_MEM_FETCH_PROT,
			
		 UC_MEM_READ_AFTER,
}

public enum uc_prot
{
    UC_PROT_ALL=7
}
